/************************************************************
 * Motorola Serial Peripheral Interface (SPI)
 *
 * @file   gkt_spi.h
 ************************************************************/

#ifndef GKT_SPI_H
#define GKT_SPI_H

#include "gkt_types.h"

#ifdef __cplusplus
extern "C" {
#endif

/************************************************************
 * SPI Mode
 ************************************************************/
typedef enum _gkt_spi_mode {
	GKT_SPI_MODE_STANDARD = 0,
	GKT_SPI_MODE_DUAL,	/* Enhance Mode */
	GKT_SPI_MODE_QUAD,	/* Enhance Mode */
	GKT_SPI_MODE_OCTAL,	/* Enhance Mode */
	GKT_SPI_MODE_MAX
} gkt_spi_mode_e;

/************************************************************
 * SPI Clock Mode
 ************************************************************
 * CPOL: Clock Polarity
 *       level of SCLK when idle
 *       - 0: low level
 *       - 1: high level
 * CPHA: Clock Phase
 *       sampling at which edge of the SCLK
 *       - 0: the first edge
 *       - 1: the second edge
 ************************************************************
 *   MODE0: CPOL = 0, CPHA = 0
 *   MODE1: CPOL = 0, CPHA = 1
 *   MODE2: CPOL = 1, CPHA = 0
 *   MODE3: CPOL = 1, CPHA = 1
 ***********************************************************/
typedef enum _gkt_spi_clock_mode {
	GKT_SPI_CLOCK_MODE0 = 0,
	GKT_SPI_CLOCK_MODE1,
	GKT_SPI_CLOCK_MODE2,
	GKT_SPI_CLOCK_MODE3,
	GKT_SPI_CLOCK_MODE_MAX
} gkt_spi_clock_mode_e;

/* Config Flags */
#define GKT_SPI_CF_LSB		(1U << 31)	/* LSB */
#define GKT_SPI_CF_RX_SAMPLE_DELAY_1CLK	(1U << 30)	/* RX_PHASE */
#define GKT_SPI_CF_CSN_SOFT	(1U << 23)	/* [MASTER] CS# is controlled by software */
#define GKT_SPI_CF_TXD_NC	(1U << 22)	/* TXD is Not Connected */
#define GKT_SPI_CF_RXD_NC	(1U << 21)	/* RXD is Not Connected */
#define GKT_SPI_CF_NO_DMA	(1U << 20)	/* Not Use DMA */
#define GKT_SPI_CF_RW_DELAY_CLKS_MASK	0xFF	/* the delay clocks between send and receive data */

/* SPI Standard params */
typedef struct _gkt_spi_config_params {
	gkt_spi_clock_mode_e	clock_mode;
	uint32_t	data_width;		/* data frame bits */
	uint32_t	max_frequence;	/* (MASTER ONLY) max frequence the slave could support */
	uint32_t	flags;	/* config flags, ONLY the least 8 bits are  */
} gkt_spi_config_params_s;

int gkt_spi_init(void);

gkt_handle_t gkt_spi_open(int master,
				uint32_t dev_id, uint32_t slave_id);

int gkt_spi_close(gkt_handle_t handle);

int gkt_spi_config(gkt_handle_t handle, 
			gkt_spi_config_params_s *params);

int gkt_spi_set_data_width(gkt_handle_t handle, 
				uint32_t data_width);

/************************************************************
 * @function - gkt_spi_master_write_read - <SPI Standard Mode>
 * @brief	 - Write & Read(TX_RX) Operation in Standard SPI Mode
 * @params
 *	 handle 	   [in] spi handle
 *	 write_data	   [in] write data
 *	 read_buff	   [in] read buffer
 *	 size		   [in] size of write/read date frames
 *	 timeout	   [in] timeout, unit in ms
 ************************************************************/
int gkt_spi_master_write_read(gkt_handle_t handle,
			const void *write_data, void *read_buff,
			uint32_t size, uint32_t timeout);

/************************************************************
 * @function - gkt_spi_master_write - <SPI Standard Mode>
 * @brief	 - Write(TX_Only) Operation in Standard SPI Mode
 * @params
 *	 handle 	   [in] spi handle
 *	 data          [in] data to write
 *	 size		   [in] size of write date frames
 *	 timeout	   [in] timeout, unit in ms
 ************************************************************/
int gkt_spi_master_write(gkt_handle_t handle,
			const void *cmd, uint32_t cmd_size,
			const void *data, uint32_t data_size, 
			uint32_t timeout);

/************************************************************
 * @function - gkt_spi_master_read - <SPI Standard Mode>
 * @brief	 - read(RX_Only or EEPROM) Operation in Standard SPI Mode
 * @params
 *	 handle 	   [in] spi handle
 *	 cmd_data      [in] command data <EEPROM - Opcode & Address>
 *	 cmd_size	   [in] size of cmd_data, unit in bytes
 *   read_buff     [out] read buffer
 *   read_size     [in] size of read date frames
 *	 timeout	   [in] timeout, unit in ms
 ************************************************************/
int gkt_spi_master_read(gkt_handle_t handle,
			const uint8_t *cmd_data, uint32_t cmd_size,
			void *read_buff, uint32_t read_size,
			uint32_t timeout);
/************************************************************
 * master read example
 * 1. read data from external spi-flash device
 *      Opcode  = 0x03
 *      Address = 0x123456
 *    ==> 
 *      cmd_data = { 0x03, 0x12, 0x34, 0x56 }
 *      cmd_size = 4 <Bytes>
 * 2. read status from external spi-flash device
 *      Opcode = 0x05
 *      Address = <None>
 *    ==>
 *      cmd_data = { 0x05 }
 *      cmd_size = 1 <Bytes>
 *
 * 3. read data from external normal SPI device
 *      cmd_data = NULL
 *      cmd_size = 0
 *
 * pass other arguments as normal!
 ************************************************************/



/************************************************************
 * Enhanced SPI Modes (Dual, Quad, or Octal)
 ************************************************************/
typedef enum _gkt_spi_enhance_transfer_type {
	/* Both Instruction and Address will be sent in Standard SPI Mode. */
	GKT_SPI_ETT_INST_ADDR_STANDARD = 0,

	/* Instruction will be sent in Standard SPI Mode and 
	 * Address will be sent in the enhanced mode 
	 * specified by mode(DUAL/QUAD/OCTAL) */
	GKT_SPI_ETT_INST_STANDARD,

	/* Both Instruction and Address will be sent in the enhanced mode 
	 * specified by mode(DUAL/QUAD/OCTAL) */
	GKT_SPI_ETT_INST_ADDR_ENHANCE,

	GKT_SPI_ETT_MAX
} gkt_spi_enhance_transfer_type_e, gkt_spi_ett_e;

#define GKT_SPI_ENHANCE_INST_MAXBITS	16
#define GKT_SPI_ENHANCE_ADDR_MAXBITS	60
#define GKT_SPI_ENHANCE_DUMMY_MAXCLOCKS	32

typedef struct _gkt_spi_enhance_params {
	gkt_spi_mode_e	spi_mode;	/* enhance mode, GKT_SPI_MODE_DUAL/DUAL/OCTAL */
	gkt_spi_ett_e	transfer_type;	/* enhance transfer type */

	const uint32_t	*cmd_data;	/* instruction & address */
	uint32_t	inst_bits;	/* instruction bits, could not be 12-bits */
	uint32_t	addr_bits;	/* address bits */
				/* instruction or/and address could be 0 bits & 4-bits aligned */

	uint32_t	dummy_clocks;	/* be applicable to READ ONLY */
} gkt_spi_enhance_params_s;

/************************************************************
 * Enhance example of spi_enhance_params
 * 1. SPIFLASH Quad Output Fast Read
 *      CMD  = 0x6B <instruction> - Standard Mode
 *      ADDR = 0x123456  - Standard Mode
 *      DUMMY CLOCKS = 8 Cycles
 *    ==>
 *      spi_mode = GKT_SPI_MODE_QUAD
 *      transfer_type = GKT_SPI_ETT_INST_ADDR_STANDARD
 *      cmd_data = { 0x6B, 0x123456 }
 *      inst_bits = 8
 *      addr_bits = 24
 *      dummy_clocks = 8
 *
 * 2. SPIFLASH Quad I/O Fast Read
 *    The First Quad I/O Fast Read
 *      CMD  = 0xEB <instruction> - Standard Mode
 *      ADDR & M = 0x123456MM - Enhance Mode
 *      DUMMY CLOCKS = 4 Cycles
 *    ==>
 *      spi_mode = GKT_SPI_MODE_QUAD
 *      transfer_type = GKT_SPI_ETT_INST_STANDARD
 *      cmd_data = { 0xEB, 0x123456MM }
 *      inst_bits = 8
 *      addr_bits = 32
 *      dummy_clocks = 4
 *    Next Quad I/O Fast Read
 *    a> previous (M5-4) = (1, 0)
 *      ADDR & M = 0x123456MM - Enhance Mode
 *      DUMMY CLOCKS = 4 Cycles
 *    ==>
 *      spi_mode = GKT_SPI_MODE_QUAD
 *      transfer_type = GKT_SPI_ETT_INST_STANDARD
 *      cmd_data = { 0x123456MM }
 *      inst_bits = 0
 *      addr_bits = 32
 *      dummy_clocks = 4
 *    b> previous (M5-4) != (1, 0)
 *      SAME AS "The First Quad I/O Fast Read"
 *
 * 3. SPIFLASH Quad Page Program
 *    CMD = 0x32 <instruction> - Standard Mode
 *    ADDR = 0x123000  - Standard Mode
 *   ==>
 *    spi_mode = GKT_SPI_MODE_QUAD
 *    transfer_type = GKT_SPI_ETT_INST_ADDR_STANDARD
 *    cmd_data = { 0x6B, 0x123000 }
 *    inst_bits = 8
 *    addr_bits = 24
 *    dummy_clocks = <ignored>
 ************************************************************/


/************************************************************
 * @function - gkt_spi_master_enhance_write
 * @brief    - Write Operation in Enhanced SPI Modes
 * @params
 *	 handle        [in] spi handle
 *   params        [in] enhance params
 *	 write_data    [in] data to be wrote
 *	 size	       [in] size of date frames to be wrote
 *   timeout       [in] timeout, unit in ms
 ************************************************************
 * A Dual/Quad/Octal SPI write can be divided into 3 parts:
 *	 Instruction phase
 *	 Address phase
 *	 Data phase
 ************************************************************/
int gkt_spi_master_enhance_write(gkt_handle_t handle,
			gkt_spi_enhance_params_s *params, 
			const void *write_data, uint32_t size, 
			uint32_t timeout);

/************************************************************
 * @function - gkt_spi_master_enhance_read
 * @brief    - Read Operation in Enhanced SPI Modes
 * @params
 *	 handle        [in] spi handle
 *   params        [in] enhance params
 *	 read_buff     [out] buffer to store the read data
 *	 size	       [in] size of date frames to be read
 *   timeout       [in] timeout, unit in ms
 ************************************************************
 * @notes
 * A Dual/Quad/Octal SPI read can be divided into 4 phases:
 *	 Instruction phase
 *	 Address phase
 *	 Wait cycles (Dummy Clocks)
 *   Data phase
 ************************************************************/
int gkt_spi_master_enhance_read(gkt_handle_t handle,
			gkt_spi_enhance_params_s *params, 
			void *read_buff, uint32_t size, 
			uint32_t timeout);

#ifdef __cplusplus
}
#endif

#endif	/* GKT_SPI_H */

